import tkinter as tk
from tkinter import ttk, filedialog, messagebox
import socket
import threading
import json
import random
import time
import os

class OmronPLCSimulator:
    def __init__(self, root):
        self.root = root
        self.root.title("OMRON PLC 模拟器 (CP1H)")
        self.root.geometry("900x600")
        self.root.resizable(True, True)
        
        # 确保中文显示正常
        self.setup_ui_fonts()
        
        # 初始化PLC数据
        self.initialize_plc_data()
        
        # 网络服务器状态
        self.server_running = False
        self.server_thread = None
        self.client_connections = []
        
        # 数据更新线程和锁 - 确保正确初始化data_lock
        self.update_thread = None
        self.updating = False
        self.data_lock = threading.Lock()  # 关键修复：正确初始化线程锁
        
        # 创建UI
        self.create_widgets()
        
        # 配置文件路径
        self.config_file = None
    
    def setup_ui_fonts(self):
        """设置UI字体以确保中文正常显示"""
        default_font = ('SimHei', 10)
        self.root.option_add("*Font", default_font)
    
    def initialize_plc_data(self):
        """初始化PLC寄存器数据"""
        # 不同类型的寄存器
        self.registers = {
            'CIO': {},       # CIO区
            'W': {},         # 工作区
            'H': {},         # 保持区
            'A': {},         # 辅助区
            'T': {},         # 定时器
            'C': {}          # 计数器
        }
        
        # 初始化一些默认寄存器
        for i in range(10):
            self.registers['CIO'][i] = {'value': 0, 'mode': 'static', 'step': 1}
            self.registers['W'][i] = {'value': 0, 'mode': 'static', 'step': 1}
            self.registers['H'][i] = {'value': 0, 'mode': 'static', 'step': 1}
        
        # 协议和网络设置
        self.protocol = 'FINS'  # 默认FINS协议
        self.ip_address = '192.168.250.1'
        self.port = 9600 if self.protocol == 'FINS' else 502
        self.subnet = 0
        self.node = 1
    
    def create_widgets(self):
        """创建用户界面组件"""
        # 创建主框架
        main_frame = ttk.Frame(self.root, padding="10")
        main_frame.pack(fill=tk.BOTH, expand=True)
        
        # 顶部配置区域
        config_frame = ttk.LabelFrame(main_frame, text="配置", padding="10")
        config_frame.pack(fill=tk.X, pady=(0, 10))
        
        # 协议选择
        ttk.Label(config_frame, text="通信协议:").grid(row=0, column=0, padx=5, pady=5, sticky=tk.W)
        self.protocol_var = tk.StringVar(value=self.protocol)
        protocol_frame = ttk.Frame(config_frame)
        protocol_frame.grid(row=0, column=1, padx=5, pady=5, sticky=tk.W)
        ttk.Radiobutton(protocol_frame, text="FINS", variable=self.protocol_var, 
                       value="FINS", command=self.on_protocol_change).pack(side=tk.LEFT)
        ttk.Radiobutton(protocol_frame, text="MODBUS TCP", variable=self.protocol_var, 
                       value="MODBUS", command=self.on_protocol_change).pack(side=tk.LEFT)
        
        # 网络配置
        ttk.Label(config_frame, text="IP地址:").grid(row=0, column=2, padx=5, pady=5, sticky=tk.W)
        self.ip_var = tk.StringVar(value=self.ip_address)
        ttk.Entry(config_frame, textvariable=self.ip_var, width=15).grid(row=0, column=3, padx=5, pady=5)
        
        ttk.Label(config_frame, text="端口:").grid(row=0, column=4, padx=5, pady=5, sticky=tk.W)
        self.port_var = tk.StringVar(value=str(self.port))
        ttk.Entry(config_frame, textvariable=self.port_var, width=6).grid(row=0, column=5, padx=5, pady=5)
        
        # FINS协议特有配置
        self.fins_frame = ttk.Frame(config_frame)
        ttk.Label(self.fins_frame, text="子网:").pack(side=tk.LEFT, padx=5)
        self.subnet_var = tk.StringVar(value=str(self.subnet))
        ttk.Entry(self.fins_frame, textvariable=self.subnet_var, width=5).pack(side=tk.LEFT, padx=5)
        
        ttk.Label(self.fins_frame, text="节点:").pack(side=tk.LEFT, padx=5)
        self.node_var = tk.StringVar(value=str(self.node))
        ttk.Entry(self.fins_frame, textvariable=self.node_var, width=5).pack(side=tk.LEFT, padx=5)
        
        self.fins_frame.grid(row=0, column=6, padx=5, pady=5, sticky=tk.W)
        
        # 服务器控制按钮
        self.start_stop_btn = ttk.Button(config_frame, text="启动服务器", command=self.toggle_server)
        self.start_stop_btn.grid(row=0, column=7, padx=10, pady=5)
        
        # 配置文件操作按钮
        btn_frame = ttk.Frame(config_frame)
        ttk.Button(btn_frame, text="保存配置", command=self.save_config).pack(side=tk.LEFT, padx=5)
        ttk.Button(btn_frame, text="加载配置", command=self.load_config).pack(side=tk.LEFT, padx=5)
        btn_frame.grid(row=0, column=8, padx=10, pady=5)
        
        # 寄存器类型选择
        reg_type_frame = ttk.Frame(main_frame)
        reg_type_frame.pack(fill=tk.X, pady=(0, 10))
        
        ttk.Label(reg_type_frame, text="寄存器类型:").pack(side=tk.LEFT, padx=5)
        self.reg_type_var = tk.StringVar(value='CIO')
        reg_types = ['CIO', 'W', 'H', 'A', 'T', 'C']
        for reg_type in reg_types:
            ttk.Radiobutton(reg_type_frame, text=reg_type, variable=self.reg_type_var,
                          value=reg_type, command=self.update_register_table).pack(side=tk.LEFT, padx=5)
        
        # 添加寄存器按钮
        ttk.Button(reg_type_frame, text="添加寄存器", command=self.add_register).pack(side=tk.RIGHT, padx=10)
        
        # 寄存器表格
        table_frame = ttk.LabelFrame(main_frame, text="寄存器数据", padding="10")
        table_frame.pack(fill=tk.BOTH, expand=True)
        
        # 创建表格
        columns = ('address', 'value', 'mode', 'step', 'actions')
        self.reg_table = ttk.Treeview(table_frame, columns=columns, show='headings')
        
        # 设置列标题
        self.reg_table.heading('address', text='地址')
        self.reg_table.heading('value', text='值')
        self.reg_table.heading('mode', text='模式')
        self.reg_table.heading('step', text='步长/范围')
        self.reg_table.heading('actions', text='操作')
        
        # 设置列宽
        self.reg_table.column('address', width=80, anchor=tk.CENTER)
        self.reg_table.column('value', width=100, anchor=tk.CENTER)
        self.reg_table.column('mode', width=100, anchor=tk.CENTER)
        self.reg_table.column('step', width=100, anchor=tk.CENTER)
        self.reg_table.column('actions', width=120, anchor=tk.CENTER)
        
        # 添加滚动条
        scrollbar = ttk.Scrollbar(table_frame, orient=tk.VERTICAL, command=self.reg_table.yview)
        self.reg_table.configure(yscroll=scrollbar.set)
        
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.reg_table.pack(fill=tk.BOTH, expand=True)
        
        # 状态栏
        self.status_var = tk.StringVar(value="就绪")
        status_bar = ttk.Label(self.root, textvariable=self.status_var, relief=tk.SUNKEN, anchor=tk.W)
        status_bar.pack(side=tk.BOTTOM, fill=tk.X)
        
        # 存储操作按钮的字典
        self.action_buttons = {}
        
        # 初始更新表格
        self.update_register_table()
        
        # 绑定表格事件
        self.reg_table.bind('<ButtonRelease-1>', self.on_table_click)
    
    def on_protocol_change(self):
        """当协议改变时更新端口"""
        self.protocol = self.protocol_var.get()
        if self.protocol == 'FINS':
            self.port_var.set('9600')
            self.fins_frame.grid()  # 显示FINS特有配置
        else:  # MODBUS
            self.port_var.set('502')
            self.fins_frame.grid_remove()  # 隐藏FINS特有配置
    
    def update_register_table(self):
        """更新寄存器表格数据"""
        # 清空现有数据
        for item in self.reg_table.get_children():
            self.reg_table.delete(item)
        
        # 清除之前的按钮
        for btn_frame in self.action_buttons.values():
            btn_frame.destroy()
        self.action_buttons.clear()
        
        # 获取当前选择的寄存器类型
        reg_type = self.reg_type_var.get()
        
        # 添加数据到表格
        with self.data_lock:  # 使用锁确保数据安全
            for addr, data in sorted(self.registers[reg_type].items()):
                # 添加行
                item = self.reg_table.insert('', tk.END, values=(
                    addr, 
                    data['value'], 
                    '静态' if data['mode'] == 'static' else '递增' if data['mode'] == 'increment' else '随机',
                    data['step']
                ))
                
                # 创建操作按钮框架
                btn_frame = ttk.Frame(self.reg_table)
                
                # 绑定地址到按钮命令，使用lambda捕获当前addr值
                edit_btn = ttk.Button(btn_frame, text="编辑", 
                                    command=lambda a=addr: self.edit_register(a))
                edit_btn.pack(side=tk.LEFT, padx=2)
                
                delete_btn = ttk.Button(btn_frame, text="删除", 
                                      command=lambda a=addr: self.delete_register(a))
                delete_btn.pack(side=tk.LEFT, padx=2)
                
                # 存储按钮框架
                self.action_buttons[item] = btn_frame
        
        # 安排更新按钮位置
        self.root.after(10, self.update_action_buttons)
    
    def update_action_buttons(self):
        """更新操作按钮的位置"""
        for item, btn_frame in self.action_buttons.items():
            # 获取单元格位置
            bbox = self.reg_table.bbox(item, 'actions')
            if bbox:
                x, y, width, height = bbox
                # 放置按钮框架
                btn_frame.place(x=x, y=y, width=width, height=height)
    
    def on_table_click(self, event):
        """处理表格点击事件，确保点击按钮时不选中行"""
        region = self.reg_table.identify_region(event.x, event.y)
        if region == "cell":
            column = int(self.reg_table.identify_column(event.x).replace('#', ''))
            if column == 5:  # 操作列
                # 阻止选中行
                self.reg_table.selection_remove(self.reg_table.selection())
    
    def add_register(self):
        """添加新寄存器"""
        reg_type = self.reg_type_var.get()
        
        with self.data_lock:
            # 找到最大地址并加1
            if self.registers[reg_type]:
                max_addr = max(self.registers[reg_type].keys())
                new_addr = max_addr + 1
            else:
                new_addr = 0
            
            # 添加新寄存器
            self.registers[reg_type][new_addr] = {'value': 0, 'mode': 'static', 'step': 1}
        
        self.update_register_table()
        self.status_var.set(f"已添加 {reg_type}{new_addr} 寄存器")
    
    def edit_register(self, addr):
        """编辑寄存器数据"""
        reg_type = self.reg_type_var.get()
        
        with self.data_lock:
            if addr not in self.registers[reg_type]:
                messagebox.showerror("错误", f"寄存器 {reg_type}{addr} 不存在")
                return
            reg_data = self.registers[reg_type][addr].copy()
        
        # 创建编辑窗口
        edit_window = tk.Toplevel(self.root)
        edit_window.title(f"编辑 {reg_type}{addr}")
        edit_window.geometry("300x220")
        edit_window.resizable(False, False)
        edit_window.transient(self.root)
        edit_window.grab_set()
        
        # 地址显示
        ttk.Label(edit_window, text=f"寄存器: {reg_type}{addr}").pack(pady=10)
        
        # 值设置
        val_frame = ttk.Frame(edit_window)
        ttk.Label(val_frame, text="值:").pack(side=tk.LEFT, padx=5)
        val_var = tk.IntVar(value=reg_data['value'])
        ttk.Entry(val_frame, textvariable=val_var, width=10).pack(side=tk.LEFT, padx=5)
        val_frame.pack(pady=5)
        
        # 模式选择
        mode_frame = ttk.Frame(edit_window)
        ttk.Label(mode_frame, text="模式:").pack(side=tk.LEFT, padx=5)
        mode_var = tk.StringVar(value=reg_data['mode'])
        ttk.Radiobutton(mode_frame, text="静态", variable=mode_var, value="static").pack(side=tk.LEFT)
        ttk.Radiobutton(mode_frame, text="递增", variable=mode_var, value="increment").pack(side=tk.LEFT)
        ttk.Radiobutton(mode_frame, text="随机", variable=mode_var, value="random").pack(side=tk.LEFT)
        mode_frame.pack(pady=5)
        
        # 步长/范围设置
        step_frame = ttk.Frame(edit_window)
        ttk.Label(step_frame, text="步长/范围:").pack(side=tk.LEFT, padx=5)
        step_var = tk.IntVar(value=reg_data['step'])
        ttk.Entry(step_frame, textvariable=step_var, width=10).pack(side=tk.LEFT, padx=5)
        ttk.Label(step_frame, text="(递增步长/随机最大值)").pack(side=tk.LEFT, padx=5)
        step_frame.pack(pady=5)
        
        # 保存按钮
        def save_changes():
            with self.data_lock:
                self.registers[reg_type][addr]['value'] = val_var.get()
                self.registers[reg_type][addr]['mode'] = mode_var.get()
                self.registers[reg_type][addr]['step'] = step_var.get()
            
            self.update_register_table()
            edit_window.destroy()
            self.status_var.set(f"已更新 {reg_type}{addr} 寄存器")
        
        # 取消按钮
        def cancel():
            edit_window.destroy()
        
        btn_frame = ttk.Frame(edit_window)
        ttk.Button(btn_frame, text="保存", command=save_changes).pack(side=tk.LEFT, padx=10)
        ttk.Button(btn_frame, text="取消", command=cancel).pack(side=tk.LEFT, padx=10)
        btn_frame.pack(pady=10)
    
    def delete_register(self, addr):
        """删除寄存器"""
        reg_type = self.reg_type_var.get()
        
        if messagebox.askyesno("确认", f"确定要删除 {reg_type}{addr} 寄存器吗?"):
            with self.data_lock:
                if addr in self.registers[reg_type]:
                    del self.registers[reg_type][addr]
            
            self.update_register_table()
            self.status_var.set(f"已删除 {reg_type}{addr} 寄存器")
    
    def toggle_server(self):
        """启动或停止服务器"""
        if not self.server_running:
            self.start_server()
        else:
            self.stop_server()
    
    def start_server(self):
        """启动服务器"""
        try:
            # 获取配置
            self.protocol = self.protocol_var.get()
            self.ip_address = self.ip_var.get()
            self.port = int(self.port_var.get())
            
            if self.protocol == 'FINS':
                self.subnet = int(self.subnet_var.get())
                self.node = int(self.node_var.get())
            
            # 创建服务器线程
            self.server_running = True
            self.server_thread = threading.Thread(target=self.run_server, daemon=True)
            self.server_thread.start()
            
            # 启动数据更新线程
            self.updating = True
            self.update_thread = threading.Thread(target=self.update_data, daemon=True)
            self.update_thread.start()
            
            self.start_stop_btn.config(text="停止服务器")
            self.status_var.set(f"{self.protocol} 服务器已启动，监听 {self.ip_address}:{self.port}")
            
        except Exception as e:
            messagebox.showerror("错误", f"启动服务器失败: {str(e)}")
            self.server_running = False
    
    def stop_server(self):
        """停止服务器"""
        self.server_running = False
        self.updating = False
        
        # 关闭所有客户端连接
        for conn in self.client_connections:
            try:
                conn.close()
            except:
                pass
        self.client_connections = []
        
        if self.server_thread and threading.current_thread() != self.server_thread:
            self.server_thread.join(timeout=1.0)
        
        self.start_stop_btn.config(text="启动服务器")
        self.status_var.set(f"{self.protocol} 服务器已停止")
    
    def run_server(self):
        """运行服务器，处理客户端连接"""
        try:
            # 创建服务器 socket
            server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            server_socket.bind((self.ip_address, self.port))
            server_socket.listen(5)
            server_socket.settimeout(1.0)  # 设置超时，以便能够检查server_running标志
            
            while self.server_running:
                try:
                    client_socket, client_address = server_socket.accept()
                    self.client_connections.append(client_socket)
                    self.status_var.set(f"已连接客户端: {client_address}")
                    
                    # 为每个客户端创建处理线程
                    if self.protocol == 'FINS':
                        client_thread = threading.Thread(
                            target=self.handle_fins_client, 
                            args=(client_socket, client_address),
                            daemon=True
                        )
                    else:  # MODBUS
                        client_thread = threading.Thread(
                            target=self.handle_modbus_client, 
                            args=(client_socket, client_address),
                            daemon=True
                        )
                    client_thread.start()
                    
                except socket.timeout:
                    continue  # 超时，继续循环检查server_running
                except Exception as e:
                    self.status_var.set(f"连接错误: {str(e)}")
                    break
            
            server_socket.close()
            
        except Exception as e:
            self.status_var.set(f"服务器错误: {str(e)}")
            self.server_running = False
            self.root.after(0, lambda: self.start_stop_btn.config(text="启动服务器"))
    
    def handle_fins_client(self, client_socket, client_address):
        """处理FINS协议客户端"""
        try:
            client_socket.settimeout(5.0)
            self.status_var.set(f"处理FINS客户端: {client_address}")
            
            while self.server_running:
                try:
                    # 接收数据
                    data = client_socket.recv(1024)
                    if not data:
                        break
                    
                    # 简单解析FINS命令并生成响应
                    response = self.process_fins_command(data)
                    if response:
                        client_socket.sendall(response)
                    
                except socket.timeout:
                    continue
                except Exception as e:
                    self.status_var.set(f"FINS客户端错误: {str(e)}")
                    break
            
        finally:
            if client_socket in self.client_connections:
                self.client_connections.remove(client_socket)
            client_socket.close()
            self.status_var.set(f"FINS客户端断开连接: {client_address}")
    
    def process_fins_command(self, data):
        """处理FINS命令并返回响应"""
        # 仅处理读取命令 (01 01)
        if len(data) >= 12 and data[10] == 0x01 and data[11] == 0x01:
            # 解析地址信息
            mem_area = data[12]
            addr_high = data[13]
            addr_low = data[14]
            count = (data[15] << 8) | data[16]
            
            addr = (addr_high << 8) | addr_low
            
            # 确定内存区域
            reg_type = 'CIO'
            if mem_area == 0x30:  # CIO区
                reg_type = 'CIO'
            elif mem_area == 0x31:  # W区
                reg_type = 'W'
            elif mem_area == 0x32:  # H区
                reg_type = 'H'
            
            # 构建响应
            response = bytearray()
            response.extend([0x46, 0x49, 0x4E, 0x53])  # "FINS"
            response.extend([0x00, 0x00])  # 长度后续设置
            response.extend([0x00, 0x00])  # 错误码
            response.extend([0x01, 0x01])  # 命令码
            response.extend([0x00, 0x00])  # 完成码 (0000表示成功)
            
            # 数据部分
            with self.data_lock:
                for i in range(count):
                    current_addr = addr + i
                    value = self.registers[reg_type].get(current_addr, {'value': 0})['value']
                    response.extend([(value >> 8) & 0xFF, value & 0xFF])  # 高位在前
            
            # 设置长度
            response[4] = (len(response) - 6) >> 8
            response[5] = (len(response) - 6) & 0xFF
            
            return response
        return None
    
    def handle_modbus_client(self, client_socket, client_address):
        """处理MODBUS TCP客户端"""
        try:
            client_socket.settimeout(5.0)
            self.status_var.set(f"处理MODBUS客户端: {client_address}")
            
            while self.server_running:
                try:
                    # 接收数据
                    data = client_socket.recv(1024)
                    if not data:
                        break
                    
                    # 处理MODBUS命令并生成响应
                    response = self.process_modbus_command(data)
                    if response:
                        client_socket.sendall(response)
                    
                except socket.timeout:
                    continue
                except Exception as e:
                    self.status_var.set(f"MODBUS客户端错误: {str(e)}")
                    break
            
        finally:
            if client_socket in self.client_connections:
                self.client_connections.remove(client_socket)
            client_socket.close()
            self.status_var.set(f"MODBUS客户端断开连接: {client_address}")
    
    def process_modbus_command(self, data):
        """处理MODBUS命令并返回响应"""
        # 仅处理读取保持寄存器命令 (0x03)
        if len(data) >= 8 and data[7] == 0x03:
            # 解析MODBUS帧
            transaction_id = data[0:2]
            protocol_id = data[2:4]
            length = data[4:6]
            unit_id = data[6:7]
            function_code = data[7:8]
            start_addr = (data[8] << 8) | data[9]
            count = (data[10] << 8) | data[11]
            
            # 构建响应
            response = bytearray()
            response.extend(transaction_id)
            response.extend(protocol_id)
            response.extend([0x00, count * 2 + 1])  # 长度
            response.extend(unit_id)
            response.extend(function_code)
            response.append(count * 2)  # 数据长度(字节)
            
            # 数据部分 (使用H区寄存器模拟保持寄存器)
            with self.data_lock:
                for i in range(count):
                    current_addr = start_addr + i
                    value = self.registers['H'].get(current_addr, {'value': 0})['value']
                    response.extend([(value >> 8) & 0xFF, value & 0xFF])  # 高位在前
            
            return response
        return None
    
    def update_data(self):
        """定期更新寄存器数据"""
        while self.updating:
            # 遍历所有寄存器，根据模式更新值
            with self.data_lock:
                for reg_type in self.registers:
                    for addr in list(self.registers[reg_type].keys()):
                        reg_data = self.registers[reg_type][addr]
                        
                        if reg_data['mode'] == 'increment':
                            reg_data['value'] += reg_data['step']
                            if reg_data['value'] > 65535:
                                reg_data['value'] = 0
                        elif reg_data['mode'] == 'random':
                            reg_data['value'] = random.randint(0, reg_data['step'])
            
            # 更新UI
            self.root.after(0, self.update_register_values)
            
            # 每秒更新一次
            time.sleep(1)
    
    def update_register_values(self):
        """更新表格中的寄存器值"""
        reg_type = self.reg_type_var.get()
        
        with self.data_lock:
            for item in self.reg_table.get_children():
                addr = int(self.reg_table.item(item, 'values')[0])
                if addr in self.registers[reg_type]:
                    value = self.registers[reg_type][addr]['value']
                    mode_text = '静态' if self.registers[reg_type][addr]['mode'] == 'static' else \
                                '递增' if self.registers[reg_type][addr]['mode'] == 'increment' else '随机'
                    step = self.registers[reg_type][addr]['step']
                    
                    self.reg_table.item(item, values=(
                        addr,
                        value,
                        mode_text,
                        step
                    ))
    
    def save_config(self):
        """保存当前配置到文件"""
        with self.data_lock:
            config = {
                'protocol': self.protocol_var.get(),
                'ip_address': self.ip_var.get(),
                'port': self.port_var.get(),
                'subnet': self.subnet_var.get(),
                'node': self.node_var.get(),
                'registers': self.registers
            }
        
        file_path = filedialog.asksaveasfilename(
            defaultextension=".json",
            filetypes=[("JSON文件", "*.json"), ("所有文件", "*.*")],
            title="保存配置"
        )
        
        if file_path:
            try:
                with open(file_path, 'w', encoding='utf-8') as f:
                    json.dump(config, f, ensure_ascii=False, indent=4)
                self.config_file = file_path
                self.status_var.set(f"配置已保存到 {os.path.basename(file_path)}")
            except Exception as e:
                messagebox.showerror("错误", f"保存配置失败: {str(e)}")
    
    def load_config(self):
        """从文件加载配置"""
        file_path = filedialog.askopenfilename(
            defaultextension=".json",
            filetypes=[("JSON文件", "*.json"), ("所有文件", "*.*")],
            title="加载配置"
        )
        
        if file_path:
            try:
                if self.server_running:
                    self.stop_server()
                
                with open(file_path, 'r', encoding='utf-8') as f:
                    config = json.load(f)
                
                self.protocol_var.set(config.get('protocol', 'FINS'))
                self.ip_var.set(config.get('ip_address', '192.168.250.1'))
                self.port_var.set(config.get('port', '9600'))
                self.subnet_var.set(config.get('subnet', '0'))
                self.node_var.set(config.get('node', '1'))
                
                with self.data_lock:
                    self.registers = config.get('registers', self.registers)
                
                self.on_protocol_change()
                self.update_register_table()
                
                self.config_file = file_path
                self.status_var.set(f"已从 {os.path.basename(file_path)} 加载配置")
                
            except Exception as e:
                messagebox.showerror("错误", f"加载配置失败: {str(e)}")

if __name__ == "__main__":
    root = tk.Tk()
    app = OmronPLCSimulator(root)
    root.mainloop()
    
